ReadWriteAccessorChain.java

package org.codefilarete.reflection;

import org.codefilarete.tool.collection.Arrays;
import org.codefilarete.tool.collection.Collections;
import org.codefilarete.tool.collection.Iterables;

import java.util.List;

/**
 * Equivalent to {@link AccessorChain} and {@link AccessorChainMutator} as an all-in-one accessor and mutator.
 * However, as a difference with {@link AccessorChainMutator}, this class is symmetric and you get access to the same
 * objet through its getter and mutator ({@link AccessorChainMutator}'s getter gives access to the penultimate object of the
 * chain). If needed, one can get a penultimate object accessor with {@link #toChainWithoutLastAccessPoint()}.
 *
 * Moreover, this class implements {@link ReadWritePropertyAccessPoint} to emphase its goal: access beans properties.
 *
 * @param <C>
 * @param <X>
 * @param <T>
 * @author Guillaume Mary
 */
public class ReadWriteAccessorChain<C, X, T> 
		implements ReadWritePropertyAccessPoint<C, T>, ValueAccessPointChain, AccessorDefinitionDefiner<C> {
	
	private final AccessorChain<C, T> accessorChain;
	private final AccessorChainMutator<C, X, T> mutatorChain;
	private final PropertyMutator<C, T> mutator;
	
	public ReadWriteAccessorChain(PropertyAccessor<C, X> rootAccessor, ReadWriteAccessPoint<X, T> mutator) {
		this.accessorChain = new AccessorChain<>(Arrays.asList(rootAccessor, mutator));
		this.mutatorChain = new AccessorChainMutator<>(Arrays.asList(rootAccessor), mutator);
		this.mutator = this.mutatorChain::set;
	}
	
	public ReadWriteAccessorChain(List<? extends Accessor<?, ?>> rootAccessors, ReadWriteAccessPoint<X, T> lastAccessPoint) {
		this.accessorChain = new AccessorChain<>(Collections.cat(rootAccessors, Arrays.asList(lastAccessPoint)));
		this.mutatorChain = new AccessorChainMutator<>(rootAccessors, lastAccessPoint);
		this.mutator = this.mutatorChain::set;
	}
	
	public ReadWriteAccessorChain(AccessorChain<C, T> accessorChain) {
		this.accessorChain = accessorChain;
		this.mutatorChain = (AccessorChainMutator<C, X, T>) accessorChain.toMutator();
		this.mutator = this.mutatorChain::set;
	}
	
	public ReadWriteAccessorChain(AccessorChainMutator<C, X, T> accessorChain) {
		this.accessorChain = accessorChain.toAccessor();
		this.mutatorChain = accessorChain;
		this.mutator = this.mutatorChain::set;
	}
	
	@Override
	public List<? extends ValueAccessPoint<?>> getAccessors() {
		return this.accessorChain.getAccessors();
	}
	
	@Override
	public PropertyAccessor<C, T> getReader() {
		return accessorChain;
	}
	
	@Override
	public PropertyMutator<C, T> getWriter() {
		return mutator;
	}
	
	public ReadWriteAccessorChain<C, ?, X> toChainWithoutLastAccessPoint() {
		ReadWriteAccessorChain<C, Object, X> result = new ReadWriteAccessorChain<>(new AccessorChain<>(Iterables.cutTail(this.accessorChain.getAccessors())));
		result.setNullValueHandler(mutatorChain.getNullValueHandler());
		return result;
	}
	
	@Override
	public ReadWriteAccessorChain<C, X, T> setNullValueHandler(AccessorChain.NullValueHandler nullValueHandler) {
		accessorChain.setNullValueHandler(nullValueHandler);
		mutatorChain.setNullValueHandler(nullValueHandler);
		return this;
	}
	
	@Override
	public boolean equals(Object other) {
		if (this == other) {
			return true;
		} else if (!(other instanceof ReadWriteAccessorChain)) {
			return super.equals(other);
		} else {
			return accessorChain.equals(((ReadWriteAccessorChain<?, ?, ?>) other).accessorChain) && this.mutatorChain.equals(((ReadWriteAccessorChain<?, ?, ?>) other).mutatorChain);
		}
	}
	
	@Override
	public int hashCode() {
		return 31 * accessorChain.hashCode() + this.mutatorChain.hashCode();
	}
	
	/**
	 * Implemented because readWriteAccessPoint::get and readWriteAccessPoint::get doesn't make good candidates for {@link AccessorDefinition}
	 * and are not decompilable by {@link AccessorDefinition} logic.
	 * 
	 * @return the {@link AccessorDefinition} of the readWriteAccessPoint chain
	 */
	@Override
	public AccessorDefinition asAccessorDefinition() {
		return AccessorDefinition.giveDefinition(this.accessorChain);
	}
	
	@Override
	public String toString() {
		return this.accessorChain.toString();
	}
}